/*Kevin Ng
 * 5/11/11
 */


import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Shape;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

import javax.imageio.ImageIO;
public class Character {
	private BufferedImage charBody;
	private BufferedImage charArm;
	private BufferedImage[] charLegs;
	private BufferedImage[] deathAnim;
	private Point point;
	private Point armPoint;
	private Point legPoint;
	@SuppressWarnings("unused")
	private Point gunPoint;
	private int nextLevel;
	private int prevLevel;
	//private int armLength;
	private int initY;
	private int legYUp;
	private int legYDown;
	private int legRight;
	private int legLeft;
	private int bodyDown;
	private int bodyUp;
	private int lives;
	private int health;
	private int yPos;
	private int stall;
	private int delay;
	private int previousClick;
	private int armLeft;
	private int armRight;
	private int armUp;
	private int armDown;
	@SuppressWarnings("unused")
	private int rotating;
	private boolean isJumping;
	private boolean isFalling;
	private boolean isDying;
	private boolean isDamaged;
	private boolean wDown;
	private boolean aDown;
	private boolean sDown;
	private boolean dDown;
	@SuppressWarnings("unused")
	private boolean spaceDown;
	private boolean mouseDown;
	private boolean mouseClicked;
	private boolean isMoving;
	private boolean isBackwards;
	private boolean stageLeft;
	private boolean stageRight;
	private boolean isDropping;
	private boolean isSingleLevelDropping;
	private boolean right; //Which direction he's facing
    private Ellipse2D bounds;
	private final int BULLET_DELAY = 30;
	private final int STALL_TIME   = 4;
	private final int JUMP_MIDDLE  = 80;
	private final int JUMP_HEIGHT  = 105;
	private final int INITLIVES    = 5;
	private final int INITHEALTH   = 100000;
	
	
	public Character(Point point){
		try {
			charBody  = ImageIO.read(new File("rossi body.png"));
			charArm   = ImageIO.read(new File("arm.png"));
			charLegs  = splitImage(ImageIO.read(new File("legs.png")),10,1);
			deathAnim = splitImage(ImageIO.read(new File("death.png")),6,1);
			
		} catch (IOException e) {
			e.printStackTrace();
		}
		//this.armLength=24;
		this.nextLevel = 0;
		this.prevLevel = 0;
		this.point     = point;
		this.initY     = point.y;
		this.bodyDown  = point.y;
		this.bodyUp    = point.y-4;
		this.armRight  = point.x-10;
		this.armLeft   = armRight+10;
		this.armDown   = point.y-6;
		this.armUp     = armDown-4;
		this.legYDown  = point.y+10;
		this.legYUp    = legYDown-4;
		this.legRight  = point.x-2;
		this.legLeft   = legRight+7;
		this.armPoint  = new Point(armRight, armDown);
		this.legPoint  = new Point(legRight,legYDown);
		this.gunPoint  = new Point(armPoint.x+14,armPoint.y+18);
		this.lives     = INITLIVES;
		this.stall     = STALL_TIME;
		this.delay     = BULLET_DELAY;
		this.previousClick = 0;
		this.isMoving      = false;
		this.right         = true;
		this.isBackwards   = false;
		this.isDropping    = false;
		this.isSingleLevelDropping = false;
		this.health = INITHEALTH;
		this.bounds = new Ellipse2D.Double(getX()+10, getY(), getBody().getWidth()-15, getBody().getHeight());
	}
	
	//From http://www.javalobby.org/articles/ultimate-image/#10
	public static BufferedImage[] splitImage(BufferedImage img, int cols, int rows) {
		int w = img.getWidth()/cols;
		int h = img.getHeight()/rows;
		int num = 0;
		BufferedImage imgs[] = new BufferedImage[w*h];
		for(int y = 0; y < rows; y++) {
			for(int x = 0; x < cols; x++) {
				imgs[num] = new BufferedImage(w, h, img.getType());
				// Tell the graphics to draw only one block of the image
				Graphics2D g = imgs[num].createGraphics();
				g.drawImage(img, 0, 0, w, h, w*x, h*y, w*x+w, h*y+h, null);
				g.dispose();
				num++;
			}
		}
		return imgs;
	}
	//From http://www.javalobby.org/articles/ultimate-image/#10
	public BufferedImage horizontalFlip(BufferedImage img) {
		int w = img.getWidth();
		int h = img.getHeight();
		BufferedImage dimg = new BufferedImage(w, h, img.getType());
		Graphics2D g = dimg.createGraphics();
		g.drawImage(img, 0, 0, w, h, w, 0, 0, h, null);
		g.dispose();
		return dimg;
	}
	
	//From http://www.javalobby.org/articles/ultimate-image/#10
	public BufferedImage rotate(BufferedImage img, int angle) {
		int w = img.getWidth();
		int h = img.getHeight();
		BufferedImage dimg = new BufferedImage(w, h, img.getType());
		Graphics2D g = dimg.createGraphics();
		g.rotate(Math.toRadians(angle), w/2, h/2);
		g.drawImage(img, null, 0, 0);
		return dimg;
	}
	
	
	public void shoot(ArrayList<Bullet> bull, Point mouse, int frame){
		
		if(mouseDown){
			if(delay==BULLET_DELAY){
				bull.add(new Bullet(new Point(armPoint.x+14,armPoint.y+18),mouse,false));
				previousClick=frame;
			}
			delay--;
			if(delay==0){
				delay=BULLET_DELAY;
			}
		}
		if(mouseClicked){
			if((frame-previousClick)>=(BULLET_DELAY) || previousClick==0){
				bull.add(new Bullet(new Point(armPoint.x+14, armPoint.y+18),mouse,false));
				previousClick=frame;
				mouseClicked=false;
			}
		}
	}
	
	public void move(int width, int frame, Stage stage){
		int step=0;
		if(frame %2==0){
			step=2;
		}
		else{
			step=1;
		}
		if(stage.getRelativeGround(point) != this.nextLevel){
			prevLevel=this.nextLevel;
			this.nextLevel=stage.getRelativeGround(point);
			//System.out.println(stage.getRelativeGround(point));
		}
		if(stageRight|| stageLeft){
			step=3;
		}
		if(aDown && !(point.x<=0) || (stageRight && frame%2==0)){
			point.x-=step;
			armPoint.x-=step;
			armLeft-=step;
			armRight-=step;
			legPoint.x-=step;
			legRight-=step;
			legLeft-=step;
		}if (dDown && !((point.x+this.charBody.getWidth())>=width) || (stageLeft && frame%2==0)){
			point.x+=step;
			armPoint.x+=step;
			armLeft+=step;
			armRight+=step;
			legPoint.x+=step;
			legRight+=step;
			legLeft+=step;
		}if((aDown && right) || (dDown && !right)){
			isBackwards=true;	
		}else{
			isBackwards=false;
		}if(wDown && !isFalling && !isDropping && !isSingleLevelDropping){
			isJumping=true;
		}if(sDown && point.y<initY-10 && !isJumping && !isFalling){
			isDropping=true;
		}
		if(!isFalling && !isDropping && !isJumping && stage.getGround(point)>point.y+5){
			isSingleLevelDropping=true;
		}if(isSingleLevelDropping){
			yPos--;
			point.y++;
			armPoint.y++;
			legYDown++;
			legYUp++;
			legPoint.y++;
			bodyUp++;
			bodyDown++;
			armDown++;
			armUp++;
			if(!(stage.getGround(point)==point.y)){
				isSingleLevelDropping=false;
				yPos=0;
			}
		}if(isDropping && !isSingleLevelDropping){
			yPos--;
			point.y++;
			armPoint.y++;
			legYDown++;
			legYUp++;
			legPoint.y++;
			bodyUp++;
			bodyDown++;
			armDown++;
			armUp++;
			if(yPos==(prevLevel)*-1 || point.y==initY){
				//System.out.println(prevLevel);
				//System.out.println(point.y);
				isDropping=false;
				yPos=0;
			}
		}
		
		if(isJumping && !isDropping && !isSingleLevelDropping){
			if(yPos<JUMP_MIDDLE){
				yPos+=2;
				point.y-=2;
				armPoint.y-=2;
				legYDown-=2;
				legYUp-=2;
				legPoint.y-=2;
				bodyUp-=2;
				bodyDown-=2;
				armDown-=2;
				armUp-=2;
			}else if(yPos<JUMP_HEIGHT){
				yPos++;
				point.y--;	
				armPoint.y--;
				legYDown--;
				legYUp--;
				legPoint.y--;
				bodyUp--;
				bodyDown--;
				armDown--;
				armUp--;
			}else if(yPos==JUMP_HEIGHT && stall>0){
				stall--;
			}else if(yPos==JUMP_HEIGHT && stall==0){
				isFalling=true;
				isJumping=false;
				yPos--;
				point.y++;
				armPoint.y++;
				legYDown++;
				legYUp++;
				legPoint.y++;
				bodyUp++;
				bodyDown++;
				armDown++;
				armUp++;
			}
		}if(isFalling && yPos>0){
				yPos--;
				point.y++;
				armPoint.y++;
				legYDown++;
				legYUp++;
				legPoint.y++;
				bodyUp++;
				bodyDown++;
				armDown++;
				armUp++;
				if(yPos==nextLevel||point.y==stage.getGround(point)){
					yPos=0;
					isFalling=false;
					stall=STALL_TIME;
				}
		}else if(isFalling && yPos==0){
				isFalling=false;
				stall=STALL_TIME;
		}
        if(isRight()){
        	bounds= new Ellipse2D.Double(getX()+5, getY(), getBody().getWidth()-15, getBody().getHeight());
        }
        else{
        	bounds = new Ellipse2D.Double(getX()+10, getY(), getBody().getWidth()-15, getBody().getHeight());
        }
		
	}
	public void setY(int x){
		point.y=x;
	}
	
	public void setLeft(boolean x){
		stageLeft=x;
	}
	
	public void setRight(boolean x){
		stageRight=x;
	}
	
	public void setDamaged(boolean x){
		isDamaged=x;
	}
	
	public void setDying(boolean x){
		isDying=x;
	}
	
	public void setW(boolean x){
		wDown=x;
	}
	
	public void setA(boolean x){
		aDown=x;
	}
	
	public void setS(boolean x){
		sDown=x;
	}
	
	public void setD(boolean x){
		dDown=x;
	}
	
	public void setSpace(boolean x){
		spaceDown=x;
	}
	
	public void setMouseDown(boolean x){
		mouseDown=x;
	}
	
	public void setMouseClicked(boolean x){
		mouseClicked=x;
	}
	
	public void setIsMoving(boolean x){
		isMoving=x;
	}
	
	public boolean isCollided(Point p){
		if(bounds.contains(p)){
			return true;
		}
		return false;
	}
	
	public boolean isMoving(){
		return isMoving;
	}
	
	public boolean isDead(){
		return isDying;
	}
	
	public boolean isDamaged(){
		return isDamaged;
	}
	
	public BufferedImage getDeath(int index){
		return deathAnim[index];
	}
	public BufferedImage getBody(){
		if(right && !stageLeft){
			return charBody;
		}
		if(stageRight){
			return charBody;
		}
		return horizontalFlip(charBody);
	}
	
	public BufferedImage getArm(Point mousePoint){
		if(stageRight){
			armPoint.x=this.armRight;
			return charArm;
		}
		else if(stageLeft){
			armPoint.x=this.armLeft;
			return horizontalFlip(charArm);
		}
		if(!(mousePoint==null)){
			double yLine=mousePoint.y-armPoint.y-24;
			int xLine=mousePoint.x-armPoint.x-24;
			double divisor=yLine/xLine;
			int rotation =(int) Math.toDegrees(Math.atan(divisor));
			if(xLine==0){
				if(yLine<0){
					rotating=0;
					return rotate(charArm, -90);}
				else if(yLine>0){
					rotating=180;
					return rotate(charArm, 90);}
				else{
					if(right){
						rotating=90;
						return charArm;}
					else{
						rotating=270;
						return horizontalFlip(charArm);}}}
			else if(xLine>0){
				if(yLine==0){
					this.right=true;
					armPoint.setLocation(new Point(armRight, armPoint.y));
					return charArm;}
					this.right=true;
					armPoint.setLocation(new Point(armRight, armPoint.y));
					rotating=90+rotation;
					return rotate(charArm, rotation);} 
			else if(xLine<0){
				if(yLine>0){
					this.right=false;
					armPoint.setLocation(new Point(armLeft, armPoint.y));
					return horizontalFlip(rotate(charArm,-rotation));
				}
				else if(yLine<0){
					this.right=false;
					armPoint.setLocation(new Point(armLeft, armPoint.y));
					return horizontalFlip(rotate(charArm,-rotation));
				}
				else if(yLine==0){
					this.right=false;
					armPoint.setLocation(new Point(armLeft, armPoint.y));
					return horizontalFlip(charArm);
				}
			}
		}
		return charArm;
	}
	
	public BufferedImage getLegs(int frame){
		int index=0;
		if(isMoving){
			if(isBackwards){
				index=9-(frame/10)%5;
			}
			else{
				index=4+(frame/10)%5;
			}
		}
		else{
			index=(frame/20)%3;
		}
		if(stageRight){
			this.legPoint.x=this.legRight;
			return charLegs[index];
		}
		else if(stageLeft){
			legPoint.x=this.legLeft;
			return horizontalFlip(charLegs[index]);
		}
		if(isJumping || isFalling){
			point.y=this.bodyDown;
			legPoint.y=this.legYDown;
			armPoint.y=this.armDown;
			if(right){
				legPoint.x=this.legRight;
				return charLegs[0];
			}
			legPoint.x=this.legLeft;
			return horizontalFlip(charLegs[0]);
		}
		if(index==3 || index ==4 || index==7 || index==8 || index == 9){
			point.y=this.bodyUp;
			legPoint.y=this.legYUp;
			armPoint.y=this.armUp;
		}
		else{
			point.y=this.bodyDown;
			legPoint.y=this.legYDown;
			armPoint.y=this.armDown;
		}
		if(right){
			legPoint.x=this.legRight;
			return charLegs[index];
		}
		else{
			legPoint.x=this.legLeft;
			return horizontalFlip(charLegs[index]);
		}
	}
	
	public int getX(){
		return point.x;
	}
	public int getY(){
		return point.y;
	}
	
	public Point getPoint(){
		return this.point;
	}
	
	public Point getArmPoint(){
		return this.armPoint;
	}
	
	public Point getLegPoint(){
		return this.legPoint;
	}
	public int getDelay(){
		return this.BULLET_DELAY;
	}
	
	
	public boolean isRight(){
		return this.right;
	}
	public void loseHealth(){
		isDamaged=true;
		health-=10;
	}
	public int getHealth(){
		return health;
	}
	
	public int getMaxHealth(){
		return this.INITHEALTH;
	}
	public void recover(){
		isDying=false;
		health = INITHEALTH;
	}
    public Shape getBounds(){
    	return bounds;
    }
    public void loseLife(){
    	isDying=true;
    	lives--;
    }
    public int getLives(){
    	return lives;
    }
}
